#include "client.h"

#include "tuimp/faketcp.h"
#include "tuimp/clientprotocol.h"

#include "common/friend.h"
#include "common/chat.h"

#include <QString>
#include <QList>
#include <QTimer>

#define KEEP_ALIVE_INTERVAL 60000

namespace Client {

Client::Client(const QString& ip,const quint16 port) { 
	socket = new FakeTcp(ip, port);
	clientprotocol = new ClientProtocol(socket);
	/* Explicit QObject::connect call. */
	QObject::connect(clientprotocol,SIGNAL(readyAction()),this,SLOT(action()));
	keepAliveTimer= new QTimer();
	QObject::connect(keepAliveTimer,SIGNAL(timeout()),this,SLOT(sendKeepAlive()));
	keepAliveTimer->start(KEEP_ALIVE_INTERVAL);
}

Client::~Client() {
	delete socket;
	delete keepAliveTimer;
}

void Client::connect(const QString& login, const QString& password) {
	ClientConnectionProtocol::connect(socket,login,password);
}

void Client::disconnect(){
	ClientConnectionProtocol::disconnect(socket);
}

void Client::changePassword(const QString& oldPassword, const QString& newPassword) {
	ClientUserProtocol::changePassword(socket, oldPassword, newPassword);
}

void Client::signup(const QString& login, const QString& password) {
	ClientUserProtocol::signup(socket,login,password);
}

void Client::changeStatus(Status status) {
	ClientUserProtocol::changeStatus(socket, status);
}

void Client::removeFriend(Friend *friend_){
	ClientFriendProtocol::remove(socket,friend_);
}

void Client::startChat(int id) {
	ClientChatProtocol::start(socket,id);
}

void Client::quitChat(Chat *chat) {
	ClientChatProtocol::exit(socket, chat);
}

void Client::inviteChat(Chat* chat, int friendId) {
	ClientChatProtocol::invite(socket, chat, friendId);
}

void Client::inviteFriend(const QString &friendLogin) {
	ClientFriendProtocol::invite(socket, friendLogin);
}

void Client::acceptInvitation(const QString &friendLogin) {
	ClientFriendProtocol::accept(socket, friendLogin);
}

void Client::denyInvitation(const QString &friendLogin) {
	ClientFriendProtocol::deny(socket, friendLogin);
}

void Client::sendKeepAlive() {
	ClientConnectionProtocol::answerKeepAlive(socket);
}

void Client::cancelConnection() {
	delete socket;
}
void Client::newOutcomingMessage(const QString &message, Chat *chat){
	ClientChatProtocol::send(socket,chat,message);
}

void Client::action() {
	while(clientprotocol->hasReadyActions()) {
		ClientProtocolAction* cpa = clientprotocol->getAction();
		/* An new action is tested to verify of wich type it is. Then another methods treat it. */
		/* After treated every action emits an signal to be caught in the interface. */
		switch(cpa->getType()) {
			case TConnection: {
				ClientConnectionAction* cca = static_cast<ClientConnectionAction*>(cpa);
				connectionAction(cca);
				break;
			}
			case TChat: {
				ClientChatAction* cha = static_cast<ClientChatAction*>(cpa);
				chatAction(cha);
				break;
			}
			case TUser: {
				ClientUserAction* cua = static_cast<ClientUserAction*>(cpa);
				userAction(cua);
				break;
			}
			case TFriend: {
				ClientFriendAction* cfa = static_cast<ClientFriendAction*>(cpa);
				friendAction(cfa);
				break;
			}
		}
		delete cpa;
	}
}

void Client::connectionAction(ClientConnectionAction* cca) { 
	switch(cca->getFunction()) {
		case CFDeny: {
			emit CDenied();
			break;
		}
		case CFAccept: {
			emit CAccepted(cca->getFriend());
			break;
		}
	}
}

void Client::chatAction(ClientChatAction* cha) {
	switch(cha->getFunction()) {
		case HFStart: {
			Chat* chat = cha->getChat();
			chats.insert(chat->getId(), cha->getChat());
			/* For each friend wich in the new chat, it is store the chats it is in. */
			/* Here it puts the new chat id in each of participants chat. */
			foreach(Friend* _friend, chat->getFriends()) {
				if(_friend) {
					friendChat.insert(_friend->getId(),chat->getId());
				}
			}
			emit HFStarted(cha->getChat());
			break;
		}
		case HFSend: {
			int id = cha->getChat()->getId();
			Chat* chat = chats[id];
			emit HFSent(chat, cha->getFriend(), cha->getMessage());	
			break;
		}
		case HFExit: {
			friendChat.remove(cha->getFriend()->getId(),cha->getChat()->getId());
			emit HFExited(cha->getChat(), cha->getFriend());		
			break;
		}
		case HFTerminate: {
			emit HFTerminated(cha->getChat());	
			break;
		}
		case HFEnter: {
			Chat* chat = chats[cha->getChat()->getId()];
			emit HFEntered(chat, cha->getFriend());
			break;
		}
	}
}

void Client::userAction(ClientUserAction* cua) {
	switch(cua->getFunction()) {
		case UFFriendStatus: {
			/* Takes every chats wich frien is in and inserts in a list so the interface can update the status on every chat window. */
			QList<int> values = friendChat.values(cua->getFriend()->getId());
			emit UFFriendStatusChanged(cua->getFriend(),values);	
			break;
		}
		case UFFriendList: {
			emit UFFriendsList(cua->getFriends());
			break;
		}
		case UFPassword: {
		
			break;
		}
	}
}

void Client::friendAction(ClientFriendAction* cfa) {
	switch(cfa->getFunction()) {
		case FFInvite: {
			emit FFInvitation(cfa->getLogin());
			break;
		}
		case FFAdd: {
			emit FFAddition(cfa->getFriend());
			break;
		}
		case FFDeny: {
			emit FFDenial(cfa->getLogin());
			break;
		}
		case FFRemove: {
			/* Takes every chats wich frien is in and inserts in a list so the interface can remove the removed friend from every chat window. */
			QList<int> chats = friendChat.values(cfa->getFriend()->getId());
			emit FFRemoval(cfa->getFriend(), chats);
			break;
		}
	}
}

}

